" --------------------------
" matchup
" --------------------------
if g:has_popup_floating
    let g:matchup_matchparen_offscreen = {'methed': 'popup'}
else
    let g:matchup_matchparen_offscreen = {'methed': 'status_manual'}
endif
PlugAddOpt 'vim-matchup'
xnoremap <sid>(std-I) I
xnoremap <sid>(std-A) A
xmap <expr> I mode()=='<c-v>'?'<sid>(std-I)':(v:count?'':'1').'i'
xmap <expr> A mode()=='<c-v>'?'<sid>(std-A)':(v:count?'':'1').'a'
for s:v in ['', 'v', 'V', '<c-v>']
    execute 'omap <expr>' s:v.'I%' "(v:count?'':'1').'".s:v."i%'"
    execute 'omap <expr>' s:v.'A%' "(v:count?'':'1').'".s:v."a%'"
endfor
nnoremap <silent>M :MatchupWhereAmI??<Cr>
" --------------------------
" set tagstack and view tag
" --------------------------
function! s:settagstack(winnr, tagname, pos)
    if !exists('*settagstack') || !exists('*gettagstack')
        return
    endif
    silent! pclose
    if get(g:, 'check_settagstack', '') == ''
        try
            let g:check_settagstack = 't'
        catch /.*/
            let g:check_settagstack = 'a'
        endtry
    endif
    call settagstack(a:winnr, {
                \ 'curidx': gettagstack()['curidx'],
                \ 'items': [{'tagname': a:tagname, 'from': a:pos}]
                \ }, g:check_settagstack)
endfunction
function! s:view_tag(...)
    if a:0 == 0
        let tagname = expand('<cword>')
        let open_position = 'list'
    else
        let tagname = a:1
        if a:0 >= 2
            let open_position = a:2
        else
            let open_position = 'goto'
        endif
    endif
    try
        let found_symbol = preview#quickfix_list(tagname, 0, &filetype)
    catch /.*/
        let found_symbol = 0
    endtry
    if found_symbol
        silent! pclose
        if open_position == 'list'
            execute "copen " . g:asyncrun_open
        else
            if open_position != 'goto'
                if open_position == 'tabe'
                    tabe %
                else
                    execute open_position
                endif
            endif
            execute "tag " . tagname
            if open_position != 'tabe'
                call feedkeys('zz')
            endif
        endif
    endif
    return found_symbol
endfunction
" --------------------------
" use lsp tag to find defs
" --------------------------
function! SymbolOrTagOrSearchAll(find_type, ...) abort
    let tagname = expand('<cword>')
    let winnr = winnr()
    let pos = getcurpos()
    let pos[0] = bufnr('')
    let find_type = a:find_type
    if a:0 == 1
        let open_position = a:1
    else
        let open_position = 'goto'
    endif
    if index(['definition', 'references', 'type_defition', 'implementation', 'declaration', 'tags', 'hover'], find_type) < 0
        let find_type = 'definition'
    endif
    if index(['tabe', 'split', 'vsplit', 'list', 'goto'], open_position) < 0
        let open_position = 'goto'
    endif
    " --------------------------
    " coc
    " --------------------------
    if g:complete_engine == 'coc'
        if find_type == 'tags' || find_type == 'hover'
            let found_symbol = 0
        else
            let commands_dict = {
                        \ 'definition' : ['definitions', 'jumpDefinition'],
                        \ 'references' : ['references', 'jumpReferences'],
                        \ 'type_defition' : ['typeDefinitions', 'jumpTypeDefinition'],
                        \ 'implementation' : ['implementations', 'jumpImplementation'],
                        \ 'declaration' : ['declarations', 'jumpDeclaration'],
                        \ }
            let [handler, jump_command] = commands_dict[find_type]
            try
                let res = CocAction(handler)
            catch /.*/
                let res = []
            endtry
            if empty(res)
                let found_symbol = 0
            else
                let found_symbol = 1
                if open_position == 'list'
                    call CocAction(jump_command, v:false)
                else
                    call s:settagstack(winnr, tagname, pos)
                    let coc_command = printf('call CocAction("%s")', jump_command)
                    if open_position == 'goto'
                        call execute(coc_command)
                    elseif open_position == 'tabe'
                        let coc_command = 'vsplit | ' . coc_command
                        call execute(coc_command)
                        call feedkeys("\<C-w>T")
                    else
                        let coc_command = open_position . ' | ' . coc_command
                        call execute(coc_command)
                        call feedkeys("zz")
                    endif
                    echohl WarningMsg | echom "found by coc " . jump_command | echohl None
                endif
            endif
        endif
    " --------------------------
    " cmp
    " --------------------------
    elseif g:complete_engine == 'cmp'
        if find_type == 'tags' || find_type == 'hover'
            let found_symbol = 0
        else
            let commands_dict = {
                        \ 'definition' : ['textDocument/definition', 'Glance definitions'],
                        \ 'references' : ['textDocument/references', 'Glance references'],
                        \ 'type_defition' : ['textDocument/typeDefinition', 'Glance type_definitions'],
                        \ 'implementation' : ['textDocument/implementation', 'Glance implementations'],
                        \ 'declaration' : ['textDocument/declaration', 'FzfDeclarations'],
                        \ }
            let [handler, float_command] = commands_dict[find_type]
            try
                let res = luaeval(printf("vim.lsp.buf_request_sync(0, '%s', vim.lsp.util.make_position_params())", handler))
            catch /.*/
                let res = []
            endtry
            if empty(res) || empty(get(res[0], 'result', []))
                let found_symbol = 0
            else
                let found_symbol = 1
                if open_position == 'list'
                    call execute(float_command)
                else
                    call s:settagstack(winnr, tagname, pos)
                    let lua_command = printf('lua vim.lsp.buf.%s()', find_type)
                    if open_position == 'goto'
                        call execute(lua_command)
                    elseif open_position == 'tabe'
                        let lua_command = 'vsplit | ' . lua_command
                        call execute(lua_command)
                        call feedkeys("\<C-w>T")
                    else
                        let lua_command = open_position . ' | ' . lua_command
                        call execute(lua_command)
                        call feedkeys("zz")
                    endif
                    echohl WarningMsg | echom "found by vim.lsp.buf." . find_type  | echohl None
                endif
            endif
        endif
    else
        let found_symbol = 0
    endif
    if find_type == "references" && get(g:, 'symbol_tool', '') =~ 'plus' && found_symbol == 0 && open_position == 'list'
        try
            call <SID>GscopeFind(0, 't')
        catch
            let found_symbol = 0
        endtry
    endif
    " tags
    if g:ctags_type != '' && found_symbol == 0
        if find_type == 'hover'
            try
                if &rtp =~ 'vim-quickui'
                    let found_symbol = quickui#tools#preview_tag(tagname, v:false) == 0
                else
                    let found_symbol = preview#preview_tag(tagname) == 0
                endif
            catch /.*/
                let found_symbol = 0
            endtry
        else
            let found_symbol = s:view_tag(tagname, open_position)
        endif
    endif
    " GrepperSearchAll
    if found_symbol == 0
        execute 'GrepperSearchAll ' . tagname
    endif
endfunction
" ---------------
" lsp or tag
" ---------------
nnoremap <silent><M-c> :call SymbolOrTagOrSearchAll("definition", "list")<Cr>
nnoremap <silent><C-h> :call SymbolOrTagOrSearchAll("hover", "list")<Cr>
" definition
nnoremap <silent><C-g> :call SymbolOrTagOrSearchAll("definition")<Cr>
nnoremap <silent><C-]> :call SymbolOrTagOrSearchAll("definition", "vsplit")<Cr>
nnoremap <silent>g<Cr> :call SymbolOrTagOrSearchAll("definition", "split")<Cr>
nnoremap <silent>g<Tab> :call SymbolOrTagOrSearchAll("definition", "tabe")<Cr>
" references
nnoremap <silent><M-/> :call SymbolOrTagOrSearchAll("references", "list")<Cr>
if InstalledCoc() || InstalledCmp() && InstalledNvimLsp()
    " declaration
    nnoremap <silent><M-C> :call SymbolOrTagOrSearchAll("declaration", "list")<Cr>
    " implementation
    nnoremap <silent><M-.> :call SymbolOrTagOrSearchAll("implementation", "list")<Cr>
    " typeDefinition
    nnoremap <silent><M-'> :call SymbolOrTagOrSearchAll("type_definition", "list")<Cr>
endif
" TODO <M-:> <M-?> is preserved
